# -*- coding: utf-8 -*-
"""
Windows Process Spy - 通用打包脚本
自动检测Python环境并打包程序
"""

import os
import sys
import subprocess
import shutil
import glob
from pathlib import Path


def find_python_executable():
    """查找可用的Python可执行文件"""
    possible_paths = []
    
    # 优先使用虚拟环境的Python（最高优先级）
    venv_python = os.path.join(os.path.dirname(__file__), '.venv', 'Scripts', 'python.exe')
    if os.path.exists(venv_python):
        possible_paths.append(venv_python)
        print("检测到虚拟环境: {}".format(venv_python))
    
    # 用户指定的Python路径
    user_python_path = r"C:\Users\lvan\AppData\Local\Programs\Python\Python313\python.exe"
    if os.path.exists(user_python_path):
        possible_paths.append(user_python_path)
    
    # 当前Python解释器
    if sys.executable:
        possible_paths.append(sys.executable)
    
    # 系统PATH中的python
    try:
        result = subprocess.run(['where', 'python'], capture_output=True, text=True)
        if result.returncode == 0:
            for line in result.stdout.strip().split('\n'):
                if line.strip():
                    possible_paths.append(line.strip())
    except:
        pass
    
    # 系统PATH中的python3
    try:
        result = subprocess.run(['where', 'python3'], capture_output=True, text=True)
        if result.returncode == 0:
            for line in result.stdout.strip().split('\n'):
                if line.strip():
                    possible_paths.append(line.strip())
    except:
        pass
    
    # JetBrains IDE Python路径
    jetbrains_paths = [
        os.path.expanduser("~/AppData/Local/JetBrains/Toolbox/apps/PyCharm*/python/python.exe"),
        os.path.expanduser("~/AppData/Local/JetBrains/Toolbox/apps/PyCharm*/python/python3.exe"),
        os.path.expanduser("~/.PyCharm*/python/python.exe"),
        os.path.expanduser("~/.PyCharm*/python/python3.exe"),
    ]
    
    for path_pattern in jetbrains_paths:
        matches = glob.glob(path_pattern)
        possible_paths.extend(matches)
    
    # 去重并检查版本
    unique_paths = []
    for path in possible_paths:
        if path not in unique_paths and os.path.exists(path):
            try:
                result = subprocess.run([path, '--version'], capture_output=True, text=True)
                if result.returncode == 0:
                    version = result.stdout.strip()
                    if 'Python 3.' in version:  # 只使用Python 3
                        unique_paths.append(path)
            except:
                continue
    
    return unique_paths


def check_python_version(python_path):
    """检查Python版本"""
    try:
        result = subprocess.run([python_path, '--version'], capture_output=True, text=True)
        if result.returncode == 0:
            version = result.stdout.strip()
            print("Python版本: {}".format(version))
            
            # 检查是否是Python 3.6+
            version_parts = version.split()
            if len(version_parts) >= 2:
                version_str = version_parts[1]
                major, minor = map(int, version_str.split('.')[:2])
                if major >= 3 and minor >= 6:
                    return True
                else:
                    print("警告: 需要Python 3.6+，当前版本可能不支持f-string语法")
                    return True  # 仍然尝试继续
            return True
    except Exception as e:
        print("检查Python版本失败: {}".format(e))
        return False


def install_dependencies(python_path):
    """安装依赖包"""
    print("\n安装依赖包...")
    
    # 首先尝试使用uv安装依赖
    try:
        print(f"使用uv安装依赖...")
        subprocess.check_call(["uv", "pip", "install", "-r", "requirements.txt"])
        print("依赖包安装成功!")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as e:
        print(f"uv安装失败: {e}")
        
    # 备用方案：使用Python的pip安装
    try:
        subprocess.check_call([python_path, "-m", "ensurepip"])
        print("已安装pip")
        subprocess.check_call([python_path, "-m", "pip", "install", "-r", "requirements.txt"])
        print("依赖包安装成功!")
        return True
    except subprocess.CalledProcessError as e:
        print(f"依赖包安装失败: {e}")
        return False


def exit_running_program():
    """退出正在运行的程序"""
    print("\n检查并退出正在运行的程序...")
    
    # 检查程序是否在运行
    gui_exe = "dist/WindowSpyGUI.exe"
    release_exe = "release/WindowSpyGUI.exe"
    
    # 尝试通过 --exit 参数退出程序
    exe_paths = []
    if os.path.exists(gui_exe):
        exe_paths.append(gui_exe)
    if os.path.exists(release_exe):
        exe_paths.append(release_exe)
    
    if not exe_paths:
        print("未找到已编译的程序，跳过退出检查")
        return
    
    # 尝试通过 --exit 命令退出程序
    for exe_path in exe_paths:
        try:
            print("尝试退出程序: {}".format(exe_path))
            # 尝试运行 --exit 命令
            result = subprocess.run(
                [exe_path, "--exit"],
                capture_output=True,
                timeout=3,
                cwd=os.path.dirname(os.path.abspath(exe_path))
            )
            if result.returncode == 0 or "退出信号已发送" in result.stdout.decode('utf-8', errors='ignore'):
                print("退出信号已发送")
        except subprocess.TimeoutExpired:
            print("程序可能在运行，等待退出...")
        except FileNotFoundError:
            print("程序文件不存在，跳过")
        except Exception as e:
            print("发送退出信号失败: {}".format(e))
    
    # 等待程序退出（最多等待5秒）
    import time
    wait_time = 0
    max_wait = 5
    while wait_time < max_wait:
        time.sleep(0.5)
        wait_time += 0.5
        # 检查进程是否还在运行
        still_running = False
        try:
            import psutil
            for proc in psutil.process_iter(['pid', 'name']):
                try:
                    proc_name = proc.info['name'].lower()
                    if 'windowspygui' in proc_name or 'python' in proc_name:
                        # 检查是否是我们的程序
                        cmdline = proc.cmdline()
                        if any('window_spy' in str(cmd) or 'WindowSpyGUI' in str(cmd) for cmd in cmdline):
                            still_running = True
                            break
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    pass
        except ImportError:
            # 如果没有psutil，简单等待
            pass
        
        if not still_running:
            print("程序已退出")
            break
        
        if wait_time >= max_wait:
            print("警告: 程序可能仍在运行，但继续打包...")
    
    time.sleep(1)  # 额外等待1秒确保文件释放


def build_program(python_path):
    """构建程序"""
    print("\n开始构建程序...")
    
    # 清理旧的构建文件
    for dir_name in ['build', 'dist']:
        if os.path.exists(dir_name):
            shutil.rmtree(dir_name)
            print("已清理目录: {}".format(dir_name))
    
    # 构建GUI版本
    print("构建GUI版本...")
    # 检查图标文件并转换为ICO格式（用于exe文件图标）
    icon_png_path = "res/icon.png"
    icon_ico_path = "res/icon.ico"
    
    # 如果PNG存在但ICO不存在，则转换
    if os.path.exists(icon_png_path) and not os.path.exists(icon_ico_path):
        try:
            from PIL import Image
            print("正在转换图标文件 PNG -> ICO...")
            img = Image.open(icon_png_path)
            # 创建ICO文件，包含多个尺寸（Windows要求）
            sizes = [(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]
            img.save(icon_ico_path, format='ICO', sizes=sizes)
            print("图标转换成功: {}".format(icon_ico_path))
        except Exception as e:
            print("警告: 图标转换失败 ({})，将使用PNG文件作为图标".format(e))
            icon_ico_path = None
    
    gui_cmd = [
        python_path, "-m", "PyInstaller",
        "--onefile",
        "--windowed",
        "--name", "WindowSpyGUI",
        "--distpath", "dist",
        "--workpath", "build",
        "--clean",
        "--noconfirm",
    ]
    
    # 设置exe文件的图标（.ico格式）
    if os.path.exists(icon_ico_path):
        gui_cmd.extend(["--icon", icon_ico_path])
        print("使用图标文件: {}".format(icon_ico_path))
    elif os.path.exists(icon_png_path):
        # 如果ICO不存在，尝试使用PNG（PyInstaller可能不支持，但可以试试）
        gui_cmd.extend(["--icon", icon_png_path])
        print("使用PNG图标文件: {}".format(icon_png_path))
    
    # 图标已嵌入到代码中，不需要额外添加数据文件
    # 但为了确保 icon_embedded.py 可以读取（开发环境），仍然可以包含
    if os.path.exists(icon_png_path):
        # Windows下使用分号分隔（开发环境时备用）
        gui_cmd.extend(["--add-data", "{};res".format(icon_png_path)])
        print("包含图标文件到数据包（开发环境备用）")
    
    # 使用新的启动器
    gui_cmd.append("main.py")
    
    try:
        subprocess.check_call(gui_cmd)
        print("GUI版本构建成功!")
    except subprocess.CalledProcessError as e:
        print("GUI版本构建失败: {}".format(e))
        return False
    
    # 构建CLI版本
    print("构建CLI版本...")
    cli_cmd = [
        python_path, "-m", "PyInstaller",
        "--onefile",
        "--console",
        "--name", "WindowSpyCLI",
        "--distpath", "dist",
        "--workpath", "build",
        "--clean",
        "--noconfirm",
        "cli.py"  # 使用新的CLI启动器
    ]
    
    try:
        subprocess.check_call(cli_cmd)
        print("CLI版本构建成功!")
    except subprocess.CalledProcessError as e:
        print("CLI版本构建失败: {}".format(e))
        return False
    
    return True


def create_release_package():
    """创建发布包"""
    print("\n创建发布包...")
    
    release_dir = "release"
    if os.path.exists(release_dir):
        try:
            # 尝试删除现有目录
            shutil.rmtree(release_dir)
        except (PermissionError, OSError) as e:
            # 如果删除失败，尝试删除目录内的文件
            print("Warning: Cannot delete release directory ({0}), trying to clean directory contents...".format(e))
            for root, dirs, files in os.walk(release_dir):
                for file in files:
                    try:
                        os.remove(os.path.join(root, file))
                    except Exception:
                        pass
                for dir_name in dirs:
                    try:
                        os.rmdir(os.path.join(root, dir_name))
                    except Exception:
                        pass
    
    # 确保目录存在
    if not os.path.exists(release_dir):
        os.makedirs(release_dir)
    
    # 复制exe文件
    exe_files = [
        "dist/WindowSpyGUI.exe",
        "dist/WindowSpyCLI.exe"
    ]
    
    # 复制资源文件（图标）
    if os.path.exists("res"):
        res_dest = os.path.join(release_dir, "res")
        if not os.path.exists(res_dest):
            os.makedirs(res_dest)
        for item in os.listdir("res"):
            src_path = os.path.join("res", item)
            dst_path = os.path.join(res_dest, item)
            if os.path.isfile(src_path):
                try:
                    shutil.copy2(src_path, dst_path)
                    print("已复制资源文件: {}".format(item))
                except Exception as e:
                    print("复制资源文件失败 {}: {}".format(item, e))
    
    for exe_file in exe_files:
        if os.path.exists(exe_file):
            dest_file = os.path.join(release_dir, os.path.basename(exe_file))
            try:
                # 如果目标文件存在，先尝试删除
                if os.path.exists(dest_file):
                    try:
                        os.remove(dest_file)
                    except (PermissionError, OSError):
                        # 如果无法删除，等待一下再试
                        import time
                        time.sleep(0.5)
                        try:
                            os.remove(dest_file)
                        except (PermissionError, OSError) as e:
                            print("警告: 无法删除目标文件 {} ({})，跳过复制...".format(dest_file, e))
                            continue
                shutil.copy2(exe_file, release_dir)
                print("已复制: {}".format(exe_file))
            except (PermissionError, OSError) as e:
                print("警告: 无法复制 {} 到 {} ({})".format(exe_file, release_dir, e))
                print("提示: 请确保release目录中的文件未被其他程序使用")
    
    # 复制文档
    docs = ["README.md", "requirements.txt"]
    for doc in docs:
        if os.path.exists(doc):
            shutil.copy2(doc, release_dir)
            print("已复制: {}".format(doc))
    
    # 创建使用说明
    usage_text = """Windows Process Spy - 使用说明

文件说明:
- WindowSpyGUI.exe: 图形界面版本，双击运行
- WindowSpyCLI.exe: 命令行版本，在命令行中运行

图形界面版本使用方法:
1. 双击 WindowSpyGUI.exe 运行
2. 点击"刷新窗口列表"查看所有窗口
3. 选择窗口查看其控件信息
4. 使用搜索框快速查找窗口

命令行版本使用方法:
WindowSpyCLI.exe --help                    # 查看帮助
WindowSpyCLI.exe --list                    # 列出所有窗口
WindowSpyCLI.exe --title "记事本"          # 查找记事本窗口
WindowSpyCLI.exe --process "notepad"       # 查找记事本进程的窗口
WindowSpyCLI.exe --hwnd 123456             # 查看指定句柄的窗口

注意事项:
- 需要Windows 10/11系统
- 某些系统窗口可能需要管理员权限才能访问
- 程序运行时请勿关闭杀毒软件

版本信息:
- 版本: 1.0.0
- 开发语言: Python 3
- 打包工具: PyInstaller
"""
    
    with open(os.path.join(release_dir, "使用说明.txt"), "w", encoding="utf-8") as f:
        f.write(usage_text)
    
    print("发布包已创建在: {}".format(release_dir))


def main():
    """主函数"""
    print("Windows Process Spy - 通用打包工具")
    print("=" * 50)
    
    # 查找Python可执行文件
    python_paths = find_python_executable()
    
    if not python_paths:
        print("错误: 未找到Python可执行文件")
        print("请确保已安装Python 3.6+")
        return
    
    print("找到以下Python环境:")
    for i, path in enumerate(python_paths, 1):
        is_venv = '.venv' in path
        marker = " [虚拟环境]" if is_venv else ""
        print("{}. {}{}".format(i, path, marker))
    
    # 选择Python环境
    # 优先使用虚拟环境
    venv_index = -1
    for i, path in enumerate(python_paths):
        if '.venv' in path:
            venv_index = i
            break
    
    if venv_index >= 0:
        selected_python = python_paths[venv_index]
        print("\n自动使用虚拟环境: {}".format(selected_python))
    elif len(python_paths) == 1:
        selected_python = python_paths[0]
        print("\n使用唯一的Python环境: {}".format(selected_python))
    else:
        # 多个Python环境且没有虚拟环境,需要用户选择
        while True:
            try:
                choice = input("\n请选择Python环境 (1-{}): ".format(len(python_paths)))
                choice_idx = int(choice) - 1
                if 0 <= choice_idx < len(python_paths):
                    selected_python = python_paths[choice_idx]
                    break
                else:
                    print("无效选择，请重新输入")
            except (ValueError, EOFError):
                # 如果无法获取输入,使用第一个
                print("\n自动使用第一个Python环境")
                selected_python = python_paths[0]
                break
    
    print("\n使用Python环境: {}".format(selected_python))
    
    # 检查Python版本
    if not check_python_version(selected_python):
        print("Python版本检查失败")
        return
    
    # 退出正在运行的程序
    exit_running_program()
    
    # 安装依赖
    if not install_dependencies(selected_python):
        print("依赖安装失败")
        return
    
    # 构建程序
    if not build_program(selected_python):
        print("程序构建失败")
        return
    
    # 创建发布包
    create_release_package()
    
    print("\n" + "=" * 50)
    print("打包完成!")
    print("\n生成的文件:")
    print("- dist/WindowSpyGUI.exe (图形界面版本)")
    print("- dist/WindowSpyCLI.exe (命令行版本)")
    print("- release/ (发布包)")
    
    # 清理编译临时文件
    print("\n" + "=" * 50)
    print("清理编译临时文件...")
    try:
        import shutil
        # 删除build目录
        if os.path.exists('build'):
            shutil.rmtree('build', ignore_errors=True)
            print("已删除 build/")
        
        # 删除__pycache__目录
        for root, dirs, files in os.walk('.'):
            if '__pycache__' in dirs:
                pycache_path = os.path.join(root, '__pycache__')
                shutil.rmtree(pycache_path, ignore_errors=True)
        print("已删除 __pycache__/")
        
        # 删除.pyc和.spec文件
        for root, dirs, files in os.walk('.'):
            for file in files:
                if file.endswith(('.pyc', '.pyo', '.spec')):
                    try:
                        os.remove(os.path.join(root, file))
                    except:
                        pass
        print("已删除 .pyc, .pyo, .spec 文件")
        
        print("\n清理完成!")
    except Exception as e:
        print("清理过程中出现错误: {}".format(e))
        print("可以手动运行 clean.bat 进行清理")


if __name__ == "__main__":
    main()
